#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>

#include "udev_include.h"
#include "worker.h"

#define MAX_EVENTS 10

static int g_udev_monitor_fd = -1;
static int g_udev_monitor_stop = 0;
static int g_udev_monitor_status = 0;
static struct udev *g_udev = NULL;
static struct udev_monitor *g_udev_monitor = NULL;

static void udev_monitor_release()
{
    if (g_udev_monitor_fd > 0) {
        close(g_udev_monitor_fd);
    }
    if (g_udev_monitor != NULL) {
        udev_monitor_unref(g_udev_monitor);
    }
    if (g_udev != NULL) {
        udev_unref(g_udev);
    }
}

static int udev_monitor_init(void)
{
    int ret = -1;

    g_udev = udev_new();
    ACTION_ON_TRUE(g_udev == NULL, return RET_ERROR);

    g_udev_monitor = udev_monitor_new_from_netlink(g_udev, "udev");
    ACTION_ON_TRUE(g_udev_monitor == NULL, return RET_ERROR);

    g_udev_monitor_fd = udev_monitor_get_fd(g_udev_monitor);
    ACTION_ON_TRUE(g_udev_monitor_fd < 0, return RET_ERROR);

    ret = udev_monitor_filter_add_match_subsystem_devtype(g_udev_monitor, "block", "disk");
    ACTION_ON_TRUE(ret != 0, return RET_ERROR);

    ret = udev_monitor_enable_receiving(g_udev_monitor);
    ACTION_ON_TRUE(ret != 0, return RET_ERROR);
    return RET_SUCCESS;
}

void udev_monitor_loop(void *ctx)
{
    int ret = -1;
    int event_cnt = 0;
    int epoll_fd = -1;
    device_info_t device_info = {0};
    struct udev_device *device = NULL;
    struct epoll_event event, events[MAX_EVENTS];

    epoll_fd = epoll_create(1);
    ACTION_ON_TRUE(epoll_fd < 0, return);

    event.data.fd = g_udev_monitor_fd;
    event.events  = EPOLLIN | EPOLLET;
    ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, g_udev_monitor_fd, &event);
    ACTION_ON_TRUE(ret != 0, return);
    g_udev_monitor_status = 1;

    while (1) {
        event_cnt = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
        ACTION_ON_TRUE(g_udev_monitor_stop, break);
        printf("event_cnt %d\n", event_cnt);

        for (int i = 0; i < event_cnt; i++) {
            ACTION_ON_TRUE(events[i].data.fd != g_udev_monitor_fd, continue);
            ACTION_ON_TRUE(!(events[i].events & EPOLLIN), continue);

            device = udev_monitor_receive_device(g_udev_monitor);
            ACTION_ON_TRUE(device == NULL, continue);
            printf("action = %s\n", udev_device_get_action(device));
            // ret = udev_get_device_info(device, &device_info);
            // if (ret != 0) {
            //     udev_device_unref(device);
            //     continue;
            // }
            udev_report_device(&device_info);
            udev_device_unref(device);
            device = NULL;
        }
    }
    g_udev_monitor_status = 0;
    udev_monitor_release();
    return;
}

int udev_monitor_start(void)
{
    int ret = -1;
    worker_t *thread = NULL;

    ret = udev_monitor_init();
    if (ret != RET_SUCCESS) {
        udev_monitor_release();
        return ret;
    }

    thread = worker_create(NULL, udev_monitor_loop);
    if (thread == NULL) {
        udev_monitor_release();
        return RET_ERROR;
    }
    sleep(50);
    g_udev_monitor_stop = 1;
    worker_destroy(thread);
    return RET_SUCCESS;
}

int udev_monitor_stop(void)
{
    g_udev_monitor_stop = 1;
    return RET_SUCCESS;
}
